﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using static DBMock.CacheType;

namespace DBMock
{
    /// <summary>
    /// 数据表builder
    /// </summary>
    public class TableBuilder
    {
        /// <summary>
        /// table name
        /// </summary>
        public string TableName { get; set; }

        /// <summary>
        /// build总数
        /// </summary>
        public long TotalCount { get; private set; }

        /// <summary>
        /// columns
        /// </summary>
        public DataColumn[] Columns { get; private set; }

        /// <summary>
        /// strategies
        /// </summary>
        public List<Strategy> Strategies { get; private set; }

        /// <summary>
        /// builder groups
        /// </summary>
        public List<Builder[]> BuilderGroups { get; private set; }

        /// <summary>
        /// validator groups
        /// </summary>
        public List<Validator[]> ValidatorGroups { get; private set; }

        /// <summary>
        /// build开始
        /// </summary>
        public event EventHandler<TableBuilderEventArgs> StartBuilding;

        /// <summary>
        /// 如果总行数大于100，在每次处理完总行数的百分之一时发生；否则处理结束时发生
        /// </summary>
        public event EventHandler<RowsBuildedEventArgs> PercentBuilded;

        /// <summary>
        /// build结束
        /// </summary>
        public event EventHandler<TableBuilderEventArgs> EndBuilding;

        /// <summary>
        /// 初始化TableBuilder
        /// </summary>
        /// <param name="entityType">实体类类型</param>
        /// <param name="totalCount">Build总数</param>
        public TableBuilder(Type entityType, long totalCount)
        {
            TableName = entityType.Name;
            TotalCount = totalCount;

            Columns = GetColumns(entityType);

            Strategies = CacheHelper.Get<List<Strategy>>(entityType.FullName, STRATEGY);
            if (Strategies == null)
            {
                Strategies = GetStrategies(entityType);
                CacheHelper.Set(entityType.FullName, Strategies, STRATEGY);
            }

            BuilderGroups = CacheHelper.Get<List<Builder[]>>(entityType.FullName, BUILDER);
            if (BuilderGroups == null)
            {
                BuilderGroups = GetBuilderGroups(entityType);
                CacheHelper.Set(entityType.FullName, BuilderGroups, BUILDER);
            }

            ValidatorGroups = CacheHelper.Get<List<Validator[]>>(entityType.FullName, VALIDATOR);
            if (ValidatorGroups == null)
            {
                ValidatorGroups = GetValidatorGroups(entityType);
                CacheHelper.Set(entityType.FullName, ValidatorGroups, VALIDATOR);
            }
        }

        /// <summary>
        /// Build数据表
        /// </summary>
        /// <returns></returns>
        public DataTable Build()
        {
            if (string.IsNullOrEmpty(TableName) || TotalCount == 0)
                return null;

            var dt = new DataTable(TableName);

            long percentCount = TotalCount / 100;
            long totalPlus = TotalCount + 1;

            if (Columns == null ||
                BuilderGroups == null || Columns.Length != BuilderGroups.Count ||
                ValidatorGroups == null || Columns.Length != ValidatorGroups.Count)
            {
                throw new Exception("Invalid defination of table.");
            }

            dt.Columns.AddRange(Columns);
            var rowBuilder = new RowBuilder(Strategies, BuilderGroups, ValidatorGroups);

            var tableArgs = new TableBuilderEventArgs(TableName, TotalCount);
            StartBuilding?.Invoke(this, tableArgs);

            for (long i = 1; i < totalPlus; i++)
            {
                var row = dt.NewRow();

                rowBuilder.Build(row);
                dt.Rows.Add(row);

                if (percentCount != 0 && i % percentCount == 0)
                {
                    var rowArgs = new RowsBuildedEventArgs(i);
                    PercentBuilded?.Invoke(this, rowArgs);
                }
            }

            EndBuilding?.Invoke(this, tableArgs);

            return dt;
        }

        //获取属性
        private PropertyInfo[] GetProperties(Type entityType)
        {
            return CacheHelper.Get(entityType.FullName, PROPERTY, n => entityType.GetProperties());
        }

        //获取列
        private DataColumn[] GetColumns(Type entityType)
        {
            var properties = GetProperties(entityType);
            if (properties == null)
                return null;

            var columns = new DataColumn[properties.Length];

            for (int i = 0; i < properties.Length; i++)
            {
                string name = properties[i].Name;
                Type type = properties[i].PropertyType;

                columns[i] = new DataColumn(name, type);
            }

            return columns;
        }

        //获取策略
        private List<Strategy> GetStrategies(Type entityType)
        {
            var properties = GetProperties(entityType);
            if (properties == null)
                return null;

            var strategies = new List<Strategy>();

            foreach (var property in properties)
            {
                var strategyAttribute = property.GetCustomAttribute<StrategyAttribute>();
                var strategy = strategyAttribute?.Strategy;

                strategies.Add(strategy);
            }

            return strategies;
        }

        //获取builder组
        private List<Builder[]> GetBuilderGroups(Type entityType)
        {
            var properties = GetProperties(entityType);
            if (properties == null)
                return null;

            var random = new Random();
            var groups = new List<Builder[]>();

            foreach (var property in properties)
            {
                var attributes = property.GetCustomAttributes<BuilderAttribute>();
                var group = attributes?
                    .Select(a => a.GetBuilder(random.Next()))?
                    .ToArray();

                groups.Add(group);
            }

            return groups;
        }

        //获取验证器
        private List<Validator[]> GetValidatorGroups(Type entityType)
        {
            var properties = GetProperties(entityType);
            if (properties == null)
                return null;

            var groups = new List<Validator[]>();

            foreach (var property in properties)
            {
                var attributes = property.GetCustomAttributes<ValidatorAttribute>();
                var group = attributes?
                    .Select(a => a.GetValidator())
                    .ToArray();

                groups.Add(group);
            }

            return groups;
        }
    }

    /// <summary>
    /// 表示传递给 EventHandler 的参数集
    /// </summary>
    public class RowsBuildedEventArgs : EventArgs
    {
        /// <summary>
        /// 获取一个值，该值返回在当前build操作期间完成的行数。
        /// </summary>
        public long RowsBuilded { get; private set; }

        /// <summary>
        /// 创建 RowsBuildedEventArgs 对象的新实例。
        /// </summary>
        /// <param name="rowsBuilded">指示在当前的build操作过程中完成的行数</param>
        public RowsBuildedEventArgs(long rowsBuilded)
        {
            RowsBuilded = rowsBuilded;
        }
    }

    /// <summary>
    /// 表示传递给EventHandler的数据表Builder参数集
    /// </summary>
    public class TableBuilderEventArgs : EventArgs
    {
        /// <summary>
        /// table name
        /// </summary>
        public string TableName { get; private set; }

        /// <summary>
        /// build总数
        /// </summary>
        public long TotalCount { get; private set; }

        /// <summary>
        /// 创建TableBuilderEventArgs的新实例
        /// </summary>
        /// <param name="entityType">实体类类型</param>
        /// <param name="totalCount">build总数</param>
        public TableBuilderEventArgs(string tableName, long totalCount)
        {
            TableName = tableName;
            TotalCount = totalCount;
        }
    }
}
